﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Net;
using System.Net.NetworkInformation;
using System.Text;
using BGSpaceShooter.View;
using BGSpaceShooterServer;
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Graphics;
using Microsoft.Xna.Framework.Input;
using XNAControls;

namespace BGSpaceShooter
{
    public class UserInterface : IDrawable
    {
        public List<XNAControl> Controls { get; set; }
        public List<XNAControl> ServerChooseControls { get; set; }
        private Panel stats;
        private Radar radar;
        private InputBox chatInput;
        private MulitlineTextBox chatTextBox;

        private Menu menu;

        private Menu scMenu;

        private InputBox address;
        private InputBox port;
        private InputBox gpAddress;
        private InputBox gpPort;

        private InputBox activeInput;

        private TextBox okButton;

        private TextBox vel;

        private Panel error;

        private int originalScreenSizeX;
        private int originalScreenSizeY;
        private int screenSizeX;
        private int screenSizeY;
        private int fullscreenSizeX = 1920;
        private int fullscreenSizeY = 1080;

        public float RadarScale { get; set; }

        private KeyboardState lastInput;

        private MouseState lastMouseState;

        private List<string> chatMessages;

        public UserInterface()
        {
            StarSphere = new StarSphere();
            Controls = new List<XNAControl>();
            screenSizeX = Display.GraphicsDevice.PresentationParameters.BackBufferWidth;
            screenSizeY = Display.GraphicsDevice.PresentationParameters.BackBufferHeight;
            originalScreenSizeX = screenSizeX;
            originalScreenSizeY = screenSizeY;

            fullscreenSizeY = Display.GraphicsDevice.DisplayMode.Height;
            fullscreenSizeX = Display.GraphicsDevice.DisplayMode.Width;

            lastInput = Keyboard.GetState();
            lastMouseState = Mouse.GetState();
            RadarScale = 0.1f;

            PrepareMenu();
            PrepareServerChooseControls();

            chatMessages = new List<string>();
        }

        public StarSphere StarSphere
        {
            get;
            set;
        }

        public UserType UserType { get; set; }

        #region IDrawable Members

        public IDrawer GetDrawer()
        {
            return new UserInterfaceDrawer(this.Controls);
        }

        #endregion

        /// <summary>
        /// Sprawdza, czy przycisk został wciśnięty od ostatniego sprawdzenia
        /// </summary>
        public bool CheckClicked(Keys key)
        {
            KeyboardState keyboardState = Keyboard.GetState();
            return keyboardState.IsKeyDown(key) && lastInput.IsKeyUp(key);
        }

        public void UpdateClicked()
        {
            lastInput = Keyboard.GetState();
        }

        public List<Keys> GetPressedKeys()
        {
            KeyboardState keyboardState = Keyboard.GetState();
            return keyboardState.GetPressedKeys().ToList();
        }

        public void PrepareMenu()
        {
            menu = new Menu();
            menu.MenuState = 0;
            menu.Background = GameContentManager.Content.GetTexture("background");
            menu.Title = GameContentManager.Content.GetTexture("title");
            menu.MenuTextures = new Texture2D[] { GameContentManager.Content.GetTexture("start_game"), GameContentManager.Content.GetTexture("exit") };
            menu.Size = new Point(Display.GraphicsDevice.PresentationParameters.BackBufferWidth,
                                  Display.GraphicsDevice.PresentationParameters.BackBufferHeight);
        }

        public void PrepareServerChooseControls()
        {
            ServerChooseControls = new List<XNAControl>();

            scMenu = new Menu();

            scMenu.Background = GameContentManager.Content.GetTexture("background");
            scMenu.Title = GameContentManager.Content.GetTexture("title");
            scMenu.MenuTextures = new Texture2D[0];
            scMenu.Size = new Point(Display.GraphicsDevice.PresentationParameters.BackBufferWidth,
                                  Display.GraphicsDevice.PresentationParameters.BackBufferHeight);
            ServerChooseControls.Add(scMenu);

            SpriteFont font = GameContentManager.Content.GetFont();

            TextBox textBox = new TextBox();
            textBox.Position = new Point(50, 210);
            textBox.Size = new Point(300, 25);
            textBox.Text = "Server Address";
            textBox.ForeColor = Color.White;
            textBox.Font = font;

            ServerChooseControls.Add(textBox);

            textBox = new TextBox();
            textBox.Position = new Point(50, 265);
            textBox.Size = new Point(300, 30);
            textBox.Text = "Server Port";
            textBox.ForeColor = Color.White;
            textBox.Font = font;

            ServerChooseControls.Add(textBox);

            textBox = new TextBox();
            textBox.Position = new Point(50, 320);
            textBox.Size = new Point(300, 30);
            textBox.Text = "GamePad Server Address";
            textBox.ForeColor = Color.White;
            textBox.Font = font;

            ServerChooseControls.Add(textBox);

            textBox = new TextBox();
            textBox.Position = new Point(50, 375);
            textBox.Size = new Point(300, 30);
            textBox.Text = "GamePad Server Port";
            textBox.ForeColor = Color.White;
            textBox.Font = font;

            ServerChooseControls.Add(textBox);

            address = new InputBox();
            address.Position = new Point(300, 210);
            address.Size = new Point(300, 30);
            address.BorderSize = 3;
            address.BackgroundTexture = GameContentManager.Content.GetTexture("UIPanel");
            address.Font = font;
            address.Visible = true;
            address.Text = "127.0.0.1";

            ServerChooseControls.Add(address);

            port = new InputBox();
            port.Position = new Point(300, 265);
            port.Size = new Point(300, 30);
            port.BorderSize = 3;
            port.BackgroundTexture = GameContentManager.Content.GetTexture("UIPanel");
            port.Font = font;
            port.Visible = true;
            port.Text = "567";

            ServerChooseControls.Add(port);

            gpAddress = new InputBox();
            gpAddress.Position = new Point(300, 320);
            gpAddress.Size = new Point(300, 30);
            gpAddress.BorderSize = 3;
            gpAddress.BackgroundTexture = GameContentManager.Content.GetTexture("UIPanel");
            gpAddress.Font = font;
            gpAddress.Visible = true;
            gpAddress.Text = "127.0.0.1";

            ServerChooseControls.Add(gpAddress);

            gpPort = new InputBox();
            gpPort.Position = new Point(300, 375);
            gpPort.Size = new Point(300, 30);
            gpPort.BorderSize = 3;
            gpPort.BackgroundTexture = GameContentManager.Content.GetTexture("UIPanel");
            gpPort.Font = font;
            gpPort.Visible = true;
            gpPort.Text = "2222";

            ServerChooseControls.Add(gpPort);

            okButton = new TextBox();
            okButton.Position = new Point(Display.GraphicsDevice.PresentationParameters.BackBufferWidth / 2, 450);
            okButton.Size = new Point(300, 30);
            okButton.Text = "Connect!";
            okButton.ForeColor = Color.GreenYellow;
            okButton.Font = font;

            ServerChooseControls.Add(okButton);

            error = new Panel();
            error.BackgroundTexture = GameContentManager.Content.GetTexture("error");
            error.Size = new Point(540, 160);
            error.Position = new Point(Display.GraphicsDevice.PresentationParameters.BackBufferWidth / 2 - 270, Display.GraphicsDevice.PresentationParameters.BackBufferHeight / 2 - 80);
            error.Visible = false;

            ServerChooseControls.Add(error);
        }

        public void PrepareControls(StarFighter starFighter, IEnumerable<StarFighter> fighters, IEnumerable<Projectile> projectiles)
        {
            Controls.Clear();

            //------------------------------------
            //Panel z HP:
            //------------------------------------
            stats = new Panel();
            stats.Position = new Point(Display.GraphicsDevice.PresentationParameters.BackBufferWidth - 170, 60);
            stats.Size = new Point(150, 60 * (starFighter.StarFighterComponents.Count + 1) + 20);
            stats.BackgroundTexture = GameContentManager.Content.GetTexture("UIPanel");

            SpriteFont font = GameContentManager.Content.GetFont();
            Texture2D hptex = GameContentManager.Content.GetTexture("HPBar");

            TextBox textBox = new TextBox();
            textBox.Position = new Point(10, 10);
            textBox.Size = new Point(130, 25);
            textBox.Text = "Hull:";
            textBox.ForeColor = Color.YellowGreen;
            textBox.Font = font;

            stats.AddChild(textBox);

            Panel panel = new Panel();
            panel.Position = new Point(10, 40);
            panel.Size = new Point(Convert.ToInt32(130 * starFighter.HealthPercent), 25);
            panel.BackgroundTexture = hptex;
            panel.SourceRectangle = new Rectangle(0, 0, Convert.ToInt32(hptex.Width * starFighter.HealthPercent), hptex.Height);

            stats.AddChild(panel);

            int hgth = 70;

            foreach (StarFighterComponent starFighterComponent in starFighter.StarFighterComponents)
            {
                textBox = new TextBox();
                textBox.Position = new Point(10, hgth);
                textBox.Size = new Point(130, 25);
                textBox.Text = starFighterComponent.MeshName + ":";
                textBox.ForeColor = Color.YellowGreen;
                textBox.Font = font;

                stats.AddChild(textBox);

                panel = new Panel();
                panel.Position = new Point(10, hgth + 30);
                panel.Size = new Point(Convert.ToInt32(130 * starFighterComponent.HealthPercent), 25);
                panel.BackgroundTexture = hptex;
                panel.SourceRectangle = new Rectangle(0, 0, Convert.ToInt32(hptex.Width * starFighterComponent.HealthPercent), hptex.Height);

                stats.AddChild(panel);

                hgth += 60;
            }

            Controls.Add(stats);

            //------------------------------------
            //Radar:
            //------------------------------------
            radar = new Radar();
            radar.Position = new Point(10, 10);
            radar.Size = new Point(100, 100);
            radar.BackgroundTexture = GameContentManager.Content.GetTexture("RadarBG");

            Texture2D ptTex = GameContentManager.Content.GetTexture("RadarPt");

            foreach (StarFighter fighter in fighters)
            {
                if (fighter == null)
                {
                    radar.AddRadarPoint(GetRadarPosition(starFighter.Position, starFighter.RotationMatrix, Vector3.Zero), ptTex, new Color(0, 0, 0, 0), -1, false);
                }
                else if (fighter != starFighter)
                {
                    radar.AddRadarPoint(GetRadarPosition(starFighter.Position, starFighter.RotationMatrix, fighter.Position), ptTex, new Color(1.0f, 0, 0, 1.0f), -1);
                }
            }

            radar.AddRadarPoint(new Vector2(0, 0), ptTex, new Color(0, 0, 1.0f, 1.0f), -1);

            Controls.Add(radar);

            vel = new TextBox();
            vel.Position = new Point(10, 120);
            vel.Size = new Point(100, 25);
            vel.Text = "V:   " + starFighter.Velocity.Length().ToString("0.00");
            vel.ForeColor = Color.YellowGreen;
            vel.Font = font;

            Controls.Add(vel);
            
            //------------------------------------
            //InputBox czatu:
            //------------------------------------
            chatInput = new InputBox();
            chatInput.Position = new Point(0, Display.GraphicsDevice.PresentationParameters.BackBufferHeight - 30);
            chatInput.Size = new Point(400, 30);
            chatInput.BorderSize = 3;
            chatInput.TextAccepted += new InputBox.Accept(chatInputTextAccepted);
            chatInput.BackgroundTexture = GameContentManager.Content.GetTexture("UIPanel");
            chatInput.Font = font;

            Controls.Add(chatInput);

            //------------------------------------
            //TextBox czatu:
            //------------------------------------
            chatTextBox = new MulitlineTextBox();
            chatTextBox.Position = new Point(0, Display.GraphicsDevice.PresentationParameters.BackBufferHeight - 180);
            chatTextBox.Size = new Point(400, 150);
            chatTextBox.Font = font;
            chatTextBox.ForeColor = Color.White;

            Controls.Add(chatTextBox);
        }

        void chatInputTextAccepted(string Text)
        {
            chatMessages.Add(Text);
        }

        private Vector2 GetRadarPosition(Vector3 curPosition, Matrix curRotation, Vector3 targetPosition)
        {
            Vector3 distance = targetPosition;

            Matrix rot = Matrix.Invert(curRotation * Matrix.CreateTranslation(curPosition));

            distance = Vector3.Transform(distance, rot);

            return (new Vector2(distance.X, distance.Z)) * RadarScale;
        }

        public void UpdateMenu()
        {
            MouseState current = Mouse.GetState();
            int menuElement = menu.CheckElementOverlapped(new Point(current.X, current.Y));

            if (menuElement != -1 && current.LeftButton == ButtonState.Pressed && lastMouseState.LeftButton == ButtonState.Released)
            {
                switch (menuElement)
                {
                    case 0:
                        Router.GameState = GameState.ServerChoose;
                        break;
                    case 1:
                        Router.GameState = GameState.End;
                        break;
                }
            }

            lastMouseState = current;
        }

        public void UpdateServerChoose()
        {
            MouseState current = Mouse.GetState();
            KeyboardState keyboard = Keyboard.GetState();

            Point pt = new Point(current.X, current.Y);
            if (current.LeftButton == ButtonState.Pressed && lastMouseState.LeftButton == ButtonState.Released)
            {
                if (address.IsOverlapping(pt))
                {
                    activeInput = address;
                }
                if (port.IsOverlapping(pt))
                {
                    activeInput = port;
                }
                if (gpAddress.IsOverlapping(pt))
                {
                    activeInput = gpAddress;
                }
                if (gpPort.IsOverlapping(pt))
                {
                    activeInput = gpPort;
                }

                if(okButton.IsOverlapping(pt))
                {
                    OkButtonClick();
                }
            }
            
            if (activeInput != null)
            {
                activeInput.HandleInput(keyboard.GetPressedKeys().Where(CheckClicked));
            }

            lastInput = keyboard;
            lastMouseState = current;
        }

        private void OkButtonClick()
        {
            try
            {
                int prt = int.Parse(port.Text);
                string ipAddress = address.Text;
                if (ipAddress.Split('.').Count() != 4)
                    throw new Exception("Bad IP");
                Ping ping = new Ping();
                if (ping.Send(IPAddress.Parse(ipAddress)).Status != IPStatus.Success)
                    throw new ArgumentException("Host niedostepny");
                int gpPrt = int.Parse(gpPort.Text);
                string gpIpAddress = gpAddress.Text;

                Router.IpAddress = ipAddress;
                Router.Port = prt;
                Router.GamePadIpAddress = gpIpAddress;
                Router.GamePadPort = gpPrt;

                Router.GameState = GameState.Initialize;
            }
            catch (Exception ex)
            {
                error.Visible = true;
            }
        }

        public void UpdateControls(StarFighter starFighter, IEnumerable<StarFighter> fighters)
        {
            int i = -1;
            foreach (XNAControl xnaControl in stats.Children)
            {
                if (xnaControl is Panel)
                {
                    Panel panel = (Panel)xnaControl;
                    if (i == -1)
                    {
                        panel.Size = new Point(Convert.ToInt32(130 * starFighter.HealthPercent), 25);
                        panel.SourceRectangle = new Rectangle(0, 0,
                                                              Convert.ToInt32(panel.BackgroundTexture.Width *
                                                                              starFighter.HealthPercent),
                                                              panel.BackgroundTexture.Height);
                    }
                    else
                    {
                        StarFighterComponent starFighterComponent = starFighter.StarFighterComponents[i];
                        panel.Size = new Point(Convert.ToInt32(130 * starFighterComponent.HealthPercent), 25);
                        panel.SourceRectangle = new Rectangle(0, 0,
                                                              Convert.ToInt32(panel.BackgroundTexture.Width *
                                                                              starFighterComponent.HealthPercent),
                                                              panel.BackgroundTexture.Height);
                    }
                    ++i;
                }
            }

            int j = 0;
            foreach (StarFighter fighter in fighters)
            {
                if (fighter == null)
                {
                    radar.UpdateRadarPoint(j, GetRadarPosition(starFighter.Position, starFighter.RotationMatrix, Vector3.Zero), new Color(0, 0, 0, 0), false);
                    ++j;
                }
                else if (fighter != starFighter)
                {
                    radar.UpdateRadarPoint(j, GetRadarPosition(starFighter.Position, starFighter.RotationMatrix, fighter.Position));
                    ++j;
                }
            }

            vel.Text = "V:   " + (starFighter.Velocity.Length() * 1000).ToString("0.00");
        }

        public void ToggleFullScreen(StarFighter playerFighter, IEnumerable<StarFighter> starFighters, IEnumerable<Projectile> projectiles)
        {
            if (!Display.GraphicsDeviceManager.IsFullScreen)
            {
                screenSizeX = fullscreenSizeX;
                screenSizeY = fullscreenSizeY;
            }
            else
            {
                screenSizeX = originalScreenSizeX;
                screenSizeY = originalScreenSizeY;
            }

            Display.GraphicsDeviceManager.PreferredBackBufferWidth = screenSizeX;
            Display.GraphicsDeviceManager.PreferredBackBufferHeight = screenSizeY;


            Display.GraphicsDeviceManager.ApplyChanges();

            Display.GraphicsDeviceManager.ToggleFullScreen();

            if (Router.GameState == GameState.Game)
            {
                PrepareControls(playerFighter, starFighters, projectiles);
            }
        }

        public void ChangeChatInputVisible()
        {
            chatInput.Visible = !chatInput.Visible;
        }

        public bool IsChatActive()
        {
            return chatInput.Visible;
        }

        public void HandleChatInput(List<Keys> keys)
        {
            chatInput.HandleInput(keys.Except(lastInput.GetPressedKeys().Except(new List<Keys> { Keys.Back, Keys.LeftShift, Keys.RightShift })));
        }

        public IEnumerable<string> GetChatMessages()
        {
            List<string> messages = new List<string>();
            foreach (string chatMessage in chatMessages)
            {
                messages.Add(chatMessage);
            }
            chatMessages.Clear();
            return messages;
        }

        public void AddNewChatMessage(string message)
        {
            chatTextBox.AppendText(message);
        }

        public void RemoveRadarPoint(int ID)
        {
            radar.RemoveRadarPoint(ID);
        }

        public IDrawer GetMenuDrawer()
        {
            return new MenuDrawer(menu);
        }

        public IDrawer GetServerChooseDrawer()
        {
            return new UserInterfaceDrawer(ServerChooseControls);
        }

        public IDrawer GetWaitDrawer()
        {
            return new MenuDrawer(scMenu);
        }
    }
}
